package com.butterfly.hotkey.worker.framework.component;

import com.butterfly.hotkey.core.constant.Constants;
import com.butterfly.hotkey.core.model.AppInfoModel;
import com.butterfly.hotkey.core.rule.ClientKeyRule;
import com.butterfly.hotkey.core.util.thread.SimpleAsyncUtils;
import com.butterfly.hotkey.worker.framework.netty.dashboard.WorkerNettyClient;
import com.butterfly.hotkey.worker.framework.netty.server.WorkerNettyServer;
import com.butterfly.hotkey.worker.framework.properties.WorkerProperties;
import com.butterfly.hotkey.worker.util.NettyIpUtil;
import com.github.benmanes.caffeine.cache.Cache;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author butterfly
 */
@Component
public class WorkerContainer {

    private static final Logger log = LoggerFactory.getLogger(Constants.WORKER);



    /**
     * 所有APP关联的关联
     * key -> appName
     * value -> keyRules
     */
    private Map<String, List<ClientKeyRule>> appRuleMap = new ConcurrentHashMap<>(256);

    /**
     * 所有APP关联的缓存器
     * key -> appName
     * value -> keyRules
     */
    private Map<String, Cache<String, Object>> appCacheMap;

    /**
     * 关联的所有Client
     */
    private final Map<String, AppInfoModel> clientMap = new ConcurrentHashMap<>(256);

    private final WorkerProperties workerProperties;

    /**
     * 关联的Dashboard
     */
    private AppInfoModel dashboard;

    private WorkerNettyClient workerNettyClient;

    public WorkerContainer(WorkerProperties workerProperties) {
        this.workerProperties = workerProperties;
    }

    @PostConstruct
    public void init() {
        SimpleAsyncUtils.execute(() -> {
            int nettyPort = workerProperties.getNettyPort();
            log.info("worker netty server is starting");
            WorkerNettyServer nodesServer = new WorkerNettyServer();
            try {
                nodesServer.startNettyServer(nettyPort);
            } catch (Exception e) {
                log.error("start worker netty server error", e);
            }
            log.info("worker netty server:{} is started", nettyPort);

            // 连接dashboard
            workerNettyClient = new WorkerNettyClient(this);
            connectDashboard();
        });
    }

    /**
     * 每分钟重连dashboard
     */
    @Scheduled(fixedDelay = 1, timeUnit = TimeUnit.MINUTES)
    public void reConnectDashboard() {
        if (dashboard == null) {
            return;
        }
        connectDashboard();
    }

    /**
     * 连接dashboard
     */
    private void connectDashboard() {
        String dashboardUrl = workerProperties.getDashboardUrl();
        workerNettyClient.connect(dashboardUrl);
    }

    /**
     * 添加dashboard的渠道信息
     *
     * @param ctx context
     */
    public synchronized void addDashboard(ChannelHandlerContext ctx) {
        log.info("dashboard:{} is connect", NettyIpUtil.clientIp(ctx));
        if (dashboard == null) {
            dashboard = new AppInfoModel(Constants.DASHBOARD);
            dashboard.add(ctx);
        }
    }

    /**
     * 添加dashboard的渠道信息
     *
     * @param ctx context
     */
    public synchronized void disconnectDashboard(ChannelHandlerContext ctx) {
        log.info("dashboard:{} is disconnect", NettyIpUtil.clientIp(ctx));
        dashboard = null;
    }

    /**
     * 添加对应APP的渠道信息
     *
     * @param appName appName
     * @param ctx     context
     */
    public synchronized void addClientApp(String appName, ChannelHandlerContext ctx) {
        log.info("new client:{},ip:{} is collect", appName, NettyIpUtil.clientIp(ctx));

        AppInfoModel appInfoModel = clientMap.get(appName);
        if (appInfoModel == null) {
            AppInfoModel appInfo = new AppInfoModel(appName);
            appInfo.add(ctx);
            clientMap.put(appName, appInfo);
            return;
        }

        // APP已存在，添加渠道
        appInfoModel.add(ctx);
    }

    /**
     * 移除对应渠道信息
     *
     * @param context context
     */
    public synchronized void removeClientApp(ChannelHandlerContext context) {
        for (Map.Entry<String, AppInfoModel> entry : clientMap.entrySet()) {
            AppInfoModel appInfoModel = entry.getValue();
            appInfoModel.remove(context);
        }
    }

    public Map<String, List<ClientKeyRule>> getAppRuleMap() {
        return appRuleMap;
    }

    public void setAppRuleMap(Map<String, List<ClientKeyRule>> appRuleMap) {
        this.appRuleMap = appRuleMap;
    }

    public Map<String, Cache<String, Object>> getAppCacheMap() {
        return appCacheMap;
    }

    public void setAppCacheMap(Map<String, Cache<String, Object>> appCacheMap) {
        this.appCacheMap = appCacheMap;
    }

    public AppInfoModel getDashboard() {
        return dashboard;
    }

    public void setDashboard(AppInfoModel dashboard) {
        this.dashboard = dashboard;
    }

    public Map<String, AppInfoModel> getClientMap() {
        return clientMap;
    }

}
